<?php

namespace System;

use System\library\Benchmark;
use System\library\Debugger;
use System\library\Logger;
use System\library\config\Config;
use System\library\loader\ClassLoader;
use System\library\error\ErrorManager;
use System\library\error\ExceptionManager;
use System\library\error\observers\Display;
use System\library\error\observers\Log;
use System\library\error\observers\Mail;
use System\library\logger\writers\Display as DisplayLog;
use System\library\pattern\Singleton;
use System\library\utility\Validate;

/**
 * <b>Class</b> :        InitSystem<br>
 * <b>Fonction</b> :     Permet l'initialisation du system : class loader, setDebug, bencmark ...
 * <br>
 * @version              1.0
 * @author               Dread <dreadlokeur@gmail.com>
 * @license              Licence publique générale GNU http://www.gnu.org/licenses/gpl.html
 * @copyright            Copyright 2011 - MidichloriansPHP and contributors
 * @package              System
 *
 * @see MidichloriansPHP\pattern\Singleton
 */
class InitSystem extends Singleton {

    /**
     * Mode debug status
     *
     * @access private
     * @static
     * @var bool
     */
    private static $_debug = false;
    /**
     * Initialisation status
     *
     * @access private
     * @static
     * @var bool
     */
    private static $_initialized = false;
    /**
     * Benchmark Temps et memoire
     *
     * @access private
     * @static
     * @var object object Benchmark class
     */
    private static $_benchmark = null;
    private static $_charset = 'UTF-8';
    private static $_language = 'fr_FR';
    private static $_timezone = 'Europe/Paris';
    /**
     * Permet d'obtenir le status du mode debug
     *
     * @access public
     * @static
     * @param void
     * @return bool attribut $_debug (status du mode)
     */
    public static function isDebug() {
        return self::$_debug;
    }

    /**
     * Permet d'obtenir le status de l'initialisation du system
     *
     * @access public
     * @static
     * @param void
     * @return bool attribut $_initialized (le status)
     */
    public static function isInitialized() {
        return self::$_initialized;
    }

    /**
     * Permet d'obtenir le benchmark
     *
     * @access public
     * @static
     * @param void
     * @return object l'object du benchmark
     */
    public static function getBenchmark() {
        return self::$_benchmark;
    }

    public static function getCharset() {
        return self::$_charset;
    }

    public static function getLanguage() {
        return self::$_language;
    }
    public static function getTimezone() {
        return self::$_timezone;
    }

    public static function setlanguage($language) {
        if (!Validate::isValidLanguage($language))
            throw new \Exception('Language parameter must be a valid Language');
        self::$_language = $language;
    }

    public static function setCharset($charset) {
        if (!Validate::isValidCharset($charset))
            throw new \Exception('Charset parameter must be a valid charset');
        self::$_charset = $charset;
    }
    public static function setTimezone($timezone) {
        if (!Validate::isValidTimezone($timezone))
            throw new \Exception('Timezone parameter must be a valid timezone');
        self::$_timezone = $timezone;
        date_default_timezone_set(self::$_timezone);
    }

    /**
     * Permet de définir le mode debug
     *
     * @access public
     * @static
     * @param bool $debug
     * @return void
     */
    public static function setDebug($debug) {
        if (!is_bool($debug))
            throw new \Exception('Debug parametre doit est de type boolean');
        self::$_debug = $debug;
        if (self::$_benchmark == null)
            self:: $_benchmark = new Benchmark(true);
    }

    /**
     * Permet de définir le mode debug
     *
     * @access public
     * @param bool $loadVendorsClass doit loader les classes externes (plugins) oui, non
     * $param array $config permet de configurer le charset, language, timezone du system
     * @return void
     */
    public function init($loadVendorsClass = true, $config = array()) {
        if (self::isInitialized())
            throw new \Exception('System is already initialized');
        $this->_registerClass($loadVendorsClass);
        $this->_loadConfig();
        $this->_setErrorManager();
        $this->_setExceptionManager();
        // Set logger setting for debugging
        $logger = Logger::getInstance();
        if (self::isDebug()) {
            $logger->setLevel(Logger::DEBUG);
            //$logger->setLogBackTrace(true);
            $logger->attach(new DisplayLog());
        }
        // Set Config system
        if(!empty($config)) {
            if(isset($config['charset']))
                self::setCharset($config['charset']);
            if(isset($config['language']))
                self::setLanguage($config['language']);
            if(isset($config['timezone']))
                self::setTimezone($config['timezone']);
        }
        $logger->debug('System initilialisée');
        self::$_initialized = true;
    }

    /**
     * Constructeur, définit le mode débug, verifie le system, et inclut les fichiers necessaires
     *
     * @access protected
     * @param bool $debug status du mode debug
     * @return void
     */
    protected function __construct($debug = false) {
        $this->_checkSystemRequirement();
        $this->_includeRequiredFiles();
        if ($debug)
            self::setDebug($debug);
    }

    /**
     * Définit le gestionnaire d'erreurs, et ajoute les observers en fonction du status du mode debug (display, ou log et mail)
     *
     * @access private
     * @param void
     * @return void
     */
    private function _setErrorManager() {
        $manager = ErrorManager::getInstance();
        // Ajout des observers
        if (self::isDebug()) {
            if (!Debugger::xdebugIsEnabled())
                $manager->start(true)->attach(new Display());
        } else {
            $manager->start(true, true)
                    ->attach(new Log())
                    ->attach(new Mail());
        }
    }

    /**
     * Définit le gestionnaire d'exceptions, et ajoute les observers en fonction du status du mode debug (display, ou log et mail)
     *
     * @access private
     * @param void
     * @return void
     */
    private function _setExceptionManager() {
        $manager = ExceptionManager::getInstance();
        // Ajout des observers
        if (self::isDebug()) {
            if (!Debugger::xdebugIsEnabled())
                $manager->start()->attach(new Display());
        }else {
            $manager->start()
                    ->attach(new Log())
                    ->attach(new Mail());
        }
    }

    /**
     * Définit le gestionnaire d'exceptions, et ajoute les observers en fonction du status du mode debug (display, ou log et mail)
     *
     * @access private
     * @param void
     * @return void
     */
    private function _checkSystemRequirement() {
        if (!file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'systemChecked')) {
            if (!version_compare(PHP_VERSION, '5.3.0', '>='))
                throw new \Exception('Erreur: vous devez avoir au minimum PHP 5.3.0');
            //Extensions php, necessaires
            $required = array(
                'SimpleXML',
                'hash',
                'gd',
                'date',
                'json',
                'session'
            );
            foreach ($required as &$val) {
                if (!extension_loaded($val))
                    throw new \Exception('Erreur: l\'extension PHP "' . $val . '" est manquante');
            }

            //cache
            if (!@file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'systemChecked', true))
                throw new \Exception('Oupss impossible de creer le cache system checked');
        }
    }

    /**
     * Permet d'inclure les fichiers necessaire pour l'initialisation du system
     *
     * @access private
     * @param void
     * @return void
     */
    private function _includeRequiredFiles() {
        include_once 'library' . DIRECTORY_SEPARATOR . 'loader' . DIRECTORY_SEPARATOR . 'ClassLoader.class.php';
        include_once 'library' . DIRECTORY_SEPARATOR . 'Benchmark.class.php';
    }

    /**
     * Permet de demarrer le loader de classes
     *
     * @access private
     * @param bool $loadVendorsClass doit loader les classes externes (plugins) oui, non
     * @return void
     */
    private function _registerClass($loadVendorsClass = true) {
        $load = new ClassLoader($loadVendorsClass);
        $load->addNamespacePath('System', __DIR__ . DIRECTORY_SEPARATOR);
        $load->register();
    }

    /**
     * Permet de charger la config du system
     *
     * @access private
     * @param void
     * @return void
     */
    private function _loadConfig() {
        Config::setCons('DS', DIRECTORY_SEPARATOR); //Utilisé pour separer les paths (compatibilité multi os)
        if (!file_exists('..' . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'main.ini'))
            throw new \Exception('Le fichier main.ini pour la configuration du system est manquant');
        Config::loadIniFile('..' . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'main.ini');
    }

}

?>
